
#include <string.h>
#include <stm32f4xx.h>
#include <stm32f4xx_ll_dma.h>

#include "share/utils.h"
#include "mcu.h"
#include "_mcu_adc.h"

/// ADC 输入通道对应的 GPIO 引脚号
static const mcu_gpio_num_t _adc_ch_pin_map[3][16] = {
	{MCU_GPIO_NUM_PA0 , MCU_GPIO_NUM_PA1 , MCU_GPIO_NUM_PA2 , MCU_GPIO_NUM_PA3 , MCU_GPIO_NUM_PA4 , MCU_GPIO_NUM_PA5 , MCU_GPIO_NUM_PA6 , MCU_GPIO_NUM_PA7 , MCU_GPIO_NUM_PB0 , MCU_GPIO_NUM_PB1 , MCU_GPIO_NUM_PC0 , MCU_GPIO_NUM_PC1 , MCU_GPIO_NUM_PC2 , MCU_GPIO_NUM_PC3 , MCU_GPIO_NUM_PC4 , MCU_GPIO_NUM_PC5}, // ADC1, ADC2
	{MCU_GPIO_NUM_PA0 , MCU_GPIO_NUM_PA1 , MCU_GPIO_NUM_PA2 , MCU_GPIO_NUM_PA3 , MCU_GPIO_NUM_PF6 , MCU_GPIO_NUM_PF7 , MCU_GPIO_NUM_PF8 , MCU_GPIO_NUM_PF9 , MCU_GPIO_NUM_PF10, MCU_GPIO_NUM_PF3 , MCU_GPIO_NUM_PC0 , MCU_GPIO_NUM_PC1 , MCU_GPIO_NUM_PC2 , MCU_GPIO_NUM_PC3 , MCU_GPIO_NUM_PF4 , MCU_GPIO_NUM_PF5}, // ADC3
};

/// ADC 通道号转换
static const uint32_t _adc_ch_map[] = {
    LL_ADC_CHANNEL_0 , LL_ADC_CHANNEL_1 , LL_ADC_CHANNEL_2 , LL_ADC_CHANNEL_3 , LL_ADC_CHANNEL_4 , LL_ADC_CHANNEL_5 , LL_ADC_CHANNEL_6 , LL_ADC_CHANNEL_7 , 
    LL_ADC_CHANNEL_8 , LL_ADC_CHANNEL_9 , LL_ADC_CHANNEL_10, LL_ADC_CHANNEL_11, LL_ADC_CHANNEL_12, LL_ADC_CHANNEL_13, LL_ADC_CHANNEL_14, LL_ADC_CHANNEL_15,
    // 仅 ADC1 有以下 3 个内部通道: 16,17,18
    LL_ADC_CHANNEL_TEMPSENSOR, ///< 用于测量芯片内部温度。其输出值需要根据手册公式换算为摄氏度。
    LL_ADC_CHANNEL_VREFINT,    ///< 用于测量内部参考电压，通常用于校准或测量供电电压。
    LL_ADC_CHANNEL_VBAT        ///< 用于测量 VBAT 管脚电压（如 RTC 备份电源）。
};

/// ADC Rank 号转换
static const uint32_t _adc_rank_map[16] = {
    LL_ADC_REG_RANK_1 , LL_ADC_REG_RANK_2 , LL_ADC_REG_RANK_3 , LL_ADC_REG_RANK_4 , LL_ADC_REG_RANK_5 , LL_ADC_REG_RANK_6 , LL_ADC_REG_RANK_7 , LL_ADC_REG_RANK_8 ,
    LL_ADC_REG_RANK_9 , LL_ADC_REG_RANK_10, LL_ADC_REG_RANK_11, LL_ADC_REG_RANK_12, LL_ADC_REG_RANK_13, LL_ADC_REG_RANK_14, LL_ADC_REG_RANK_15, LL_ADC_REG_RANK_16
};

/// ADC Rank 数量转换
static const uint32_t _adc_rankcnt_map[16] = {
    LL_ADC_REG_SEQ_SCAN_DISABLE       , LL_ADC_REG_SEQ_SCAN_ENABLE_2RANKS , LL_ADC_REG_SEQ_SCAN_ENABLE_3RANKS , LL_ADC_REG_SEQ_SCAN_ENABLE_4RANKS , LL_ADC_REG_SEQ_SCAN_ENABLE_5RANKS , LL_ADC_REG_SEQ_SCAN_ENABLE_6RANKS , LL_ADC_REG_SEQ_SCAN_ENABLE_7RANKS , LL_ADC_REG_SEQ_SCAN_ENABLE_8RANKS ,
    LL_ADC_REG_SEQ_SCAN_ENABLE_9RANKS , LL_ADC_REG_SEQ_SCAN_ENABLE_10RANKS, LL_ADC_REG_SEQ_SCAN_ENABLE_11RANKS, LL_ADC_REG_SEQ_SCAN_ENABLE_12RANKS, LL_ADC_REG_SEQ_SCAN_ENABLE_13RANKS, LL_ADC_REG_SEQ_SCAN_ENABLE_14RANKS, LL_ADC_REG_SEQ_SCAN_ENABLE_15RANKS, LL_ADC_REG_SEQ_SCAN_ENABLE_16RANKS
};

// ADC1 有16个外部通道，还有 16、17、18 这3个内部通道，分别用于测量 内部温度、参考电压、VBAT
// 为了方便使用 内部温度、参考电压、VBAT 测量功能，提供一个默认的 ADC1 实例
static mcu_adc_t _adc1_default_instance = {.adc = ADC1};
static mcu_adc_t *_adc1 = NULL;

#define ADC_RESOLUTION		4095.0f               // 12-bit ADC
static float _vref  = VREFINT_CAL_VREF / 1000.0f; // 参考电压（单位：V）
static float _fcali = 1.0f;                       // ADC 校准系数

#define TS_CAL30  (*((uint16_t*)0x1FFF7A2C)) // @30°C  时的典型电压（单位：V）
#define TS_CAL100 (*((uint16_t*)0x1FFF7A2E)) // @110°C 时的典型电压（单位：V）

float mcu_adc_vref(mcu_adc_t* dev) {
    return _vref;
}

float mcu_adc_cali(uint32_t adc_raw) {
    return adc_raw * _fcali;
}

float mcu_adc_cali_v(uint32_t adc_raw) {
    return adc_raw * _vref / ADC_RESOLUTION;
}

float mcu_adc_read_ictemp() {
    if(!_adc1) mcu_adc_init(&_adc1_default_instance, 0x70000);
    // 配置前要禁用 ADC
    LL_ADC_Disable(ADC1);
    
    // 在 STM32F4xx（包括 STM32F429）中，内部温度传感器和 VREFINT 是始终连接到 ADC 的，不需要像 F3/H7 那样用 LL_ADC_TempSensor_Enable() 显式使能。
    //LL_ADC_TempSensor_Enable(ADC1);     // 使能温度传感器
    
    // 注意: STM32F42x/STM32F43x 上 当测量 TSVREFE/TEMPSENSOR 时必须禁用 VBATE，如果使能了 VBATE 则只会转换 VBAT !!!
    // On STM32F42x and STM32F43x devices, VBATE must be disabled when TSVREFE is set. If both bits are set, only the VBAT conversion is performed
    // 注意: STM32F42x/STM32F43x 上 TSVREFE 和 TEMPSENSOR 使用相同的 CommonPath，即 LL_ADC_PATH_INTERNAL_TEMPSENSOR == LL_ADC_PATH_INTERNAL_VREFINT !!! 
    LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_TEMPSENSOR);    
    // 读取温度传感器 ADC 原始值
    uint32_t adc_raw = mcu_adc_read1(_adc1, 16);
    // 根据校准后参考电压，校准测量值
    float vadc = mcu_adc_cali(adc_raw);
    
    uint16_t temp_cal1 = *TEMPSENSOR_CAL1_ADDR;
    uint16_t temp_cal2 = *TEMPSENSOR_CAL2_ADDR;
    float temp = ((vadc - temp_cal1) * (TEMPSENSOR_CAL2_TEMP - TEMPSENSOR_CAL1_TEMP)) / (temp_cal2 - temp_cal1) + TEMPSENSOR_CAL1_TEMP; // 按手册公式
    return temp;
}

float mcu_adc_read_vref() {
    if(!_adc1) mcu_adc_init(&_adc1_default_instance, 0x70000);
    // 配置前要禁用 ADC
    LL_ADC_Disable(ADC1);
    // On STM32F42x and STM32F43x devices, VBATE must be disabled when TSVREFE is set. If both bits are set, only the VBAT conversion is performed
    LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_VREFINT);
    // 读取参考电压 ADC 原始值
    uint32_t vrefint_adc = mcu_adc_read1(_adc1, 17);
    
    // 工厂校准ADC值
    uint16_t vrefint_cal = *VREFINT_CAL_ADDR;
    // 公式：实际VREF+ = 3.3V * vrefint_cal / vrefint_adc
    // 或实际VREF+ = VREFINT_CAL_V * vrefint_cal / vrefint_adc
    _vref  = VREFINT_CAL_VREF * vrefint_cal / (1000.0f * vrefint_adc);
    _fcali = ((float)vrefint_cal) / vrefint_adc;
    return _vref;
}

float mcu_adc_read_vbat() {
    if(!_adc1) mcu_adc_init(&_adc1_default_instance, 0x70000);
    // 配置前要禁用 ADC
    LL_ADC_Disable(ADC1);
    // On STM32F42x and STM32F43x devices, VBATE must be disabled when TSVREFE is set. If both bits are set, only the VBAT conversion is performed
    LL_ADC_SetCommonPathInternalCh(__LL_ADC_COMMON_INSTANCE(ADC1), LL_ADC_PATH_INTERNAL_VBAT);
    // 读取 VBAT ADC 原始值
    uint32_t adc_raw = mcu_adc_read1(_adc1, 18);
    // 根据校准后参考电压，校准测量值
    float vadc = mcu_adc_cali_v(adc_raw);
    // 芯片内部自动将 VBAT 1/4 分压给ADC，所以要 *4
    return vadc * 4;
}

/**
 * 获取 ADC 输入通道对应的 GPIO 引脚号
 * @param adc 使用的 ADC 控制器
 * @param chidx ADC 输入通道号(0~15)
 * @return GPIO 引脚号
 */
static mcu_gpio_num_t _adc_channel_pin(mcu_adc_t* dev, uint8_t chidx) {
	if(!dev || chidx > 15) return MCU_GPIO_NUM_MAX;
	if(dev->adc == ADC1 || dev->adc == ADC2) return _adc_ch_pin_map[0][chidx];
	if(dev->adc == ADC3)                     return _adc_ch_pin_map[1][chidx];
	return MCU_GPIO_NUM_MAX;
}

size_t mcu_adc_max(mcu_adc_t* dev) {
    return (1 << 12) - 1;
}

size_t mcu_adc_bits(mcu_adc_t* dev) {
    return 12;
}

size_t mcu_adc_bytes(mcu_adc_t* dev) {
    return 2;
}

size_t mcu_adc_chcount(mcu_adc_t* dev) {
    if(!dev) return 0;
    if(dev->adc == ADC1) return 19; // ADC1 有16个外部通道，还有 16、17、18 这3个内部通道，分别用于测量 内部温度、参考电压、VBAT
    if(dev->adc == ADC2 || dev->adc == ADC2) return 16;
    return 0;
}

size_t mcu_adc_samplerate_max(mcu_adc_t* dev) {
    return 0;
}

std_err_t mcu_adc_init(mcu_adc_t *dev, uint32_t chmask) {
	if(!dev || !dev->adc || !chmask || chmask >= (1U << mcu_adc_chcount(dev))) return STD_ERR_INVALID_ARG;    

    // 配置 外部通道对应的 GPIO 引脚为模拟输入
    for(int i=0; i < 16; i++) {
        if(chmask & (1U << i)) {
            mcu_gpio_num_t gnum = _adc_channel_pin(dev, i);
            GPIO_TypeDef *gport = mcu_gpio_port(gnum);
            uint32_t gpin = mcu_gpio_pin(gnum);
            LL_GPIO_SetPinMode(gport, gpin, LL_GPIO_MODE_ANALOG);
            LL_GPIO_SetPinPull(gport, gpin, LL_GPIO_PULL_NO);
        }
    }
    dev->chmask_init = chmask;
    
    // 配置前要禁用 ADC
    LL_ADC_Disable(dev->adc);
    
    // 基本 ADC 配置
    LL_ADC_SetResolution(dev->adc, LL_ADC_RESOLUTION_12B);
    //LL_ADC_SetScanDirection(dev->adc, LL_ADC_SCAN_DIRECTION_FORWARD); // 无此函数，默认就是 forward (from lowest channel number to highest channel number).
    LL_ADC_SetDataAlignment(dev->adc, LL_ADC_DATA_ALIGN_RIGHT);
    LL_ADC_REG_SetContinuousMode(dev->adc, LL_ADC_REG_CONV_CONTINUOUS);
    LL_ADC_REG_SetDMATransfer(dev->adc, LL_ADC_REG_DMA_TRANSFER_UNLIMITED);
    LL_ADC_REG_SetTriggerSource(dev->adc, LL_ADC_REG_TRIG_SOFTWARE);
    LL_ADC_SetSequencersScanMode(dev->adc, LL_ADC_REG_SEQ_SCAN_DISABLE); // 单通道
    
    // 配置 ADC 公共部分
    LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};
    ADC_CommonInitStruct.CommonClock = LL_ADC_CLOCK_SYNC_PCLK_DIV4;
    ADC_CommonInitStruct.Multimode = LL_ADC_MULTI_INDEPENDENT;
    LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(dev->adc), &ADC_CommonInitStruct);
    
    if(dev->adc == ADC1) {
        dev->chmask_init |= 0x70000;    // 默认内部通道是已经初始化过的
        _adc1 = dev;                    // 设置为默认 ADC1
    }
    
    // 校准参考电压
    mcu_adc_read_vref();
    return STD_ERR_OK;
}

uint32_t mcu_adc_read1(mcu_adc_t* dev, uint32_t chidx) {
	if(!dev || !dev->adc || !((1U << chidx) & dev->chmask_init)) return 0;
    
    // 配置前要禁用 ADC
    LL_ADC_Disable(dev->adc);
    
    // 配置单通道
    LL_ADC_REG_SetSequencerLength(dev->adc, LL_ADC_REG_SEQ_SCAN_DISABLE);
    LL_ADC_REG_SetSequencerRanks(dev->adc, LL_ADC_REG_RANK_1, _adc_ch_map[chidx]);
    LL_ADC_SetChannelSamplingTime(dev->adc, _adc_ch_map[chidx], LL_ADC_SAMPLINGTIME_480CYCLES);

    // 使能ADC
    LL_ADC_Enable(dev->adc);

    // 1. 启动转换
    LL_ADC_REG_StartConversionSWStart(dev->adc);
    // 2. 等待转换完成
    while(!LL_ADC_IsActiveFlag_EOCS(dev->adc));
    // 3. 读取结果
    return LL_ADC_REG_ReadConversionData12(dev->adc);
}

std_err_t mcu_adc_start(mcu_adc_t* dev, uint32_t chmask, uint8_t sample_rate, void* samples[], uint32_t sample_count, bool looping) {
	if(!dev || !dev->adc || (chmask | dev->chmask_init) != dev->chmask_init || !samples || !sample_count) return STD_ERR_INVALID_ARG;
    
    // 使能的通道数量
    dev->dma_chcount = count_bit1(chmask);
    // 配置通道序列
    LL_ADC_REG_SetSequencerLength(dev->adc, _adc_rankcnt_map[dev->dma_chcount - 1]);    
    // 逐个通道设置 Rank 和 采样周期/采样率
    for(int i=0; i < 16; i++) {
        if(chmask & (1U << i)) {
            // 配置通道的 Rank 号
            LL_ADC_REG_SetSequencerRanks(dev->adc, _adc_rank_map[i], _adc_ch_map[i]);
            // 配置通道的 采样周期/采样率
            LL_ADC_SetChannelSamplingTime(dev->adc, _adc_ch_map[i], LL_ADC_SAMPLINGTIME_56CYCLES);
        }
    }
    dev->dma_chmask = chmask;
    
    // 配置 DMA
    //   Stream = 数据传输路径，Channel = 外设请求信号编号
    //   Stream 决定数据流向，Channel 决定服务哪个外设
    // 1. DMA Stream: DMA 控制器（DMA1、DMA2）每个有 8 个 stream（编号 0~7）。
    //   - 每个 stream 可以理解为一条“数据通路”，可以独立配置和启动，互不影响。
    //   - 每个 stream 可以服务于不同的外设或内存搬运任务。
    // 2. DMA Channel: 每个 stream 内部又有多条 channel（通道），STM32F429 通常每个 stream 有 8 个 channel（编号 0~7）。
    //   - 每个外设的 DMA 请求会映射到具体的 channel。
    //   - 一个 stream 一次只服务一个 channel
    //   - 不同 stream 可以选用相同或不同的 channel
    //   - 不同 stream 可并发
    //
    // ADC 对应的 DMA 资源如下表所示(官方参考手册 RM0090 Rev.18，见第2212页及 Table 61: DMA request mapping):
    // ADC	DMA     Stream	    Channel
    // ----+-----+-----------+-----------
    // ADC1	DMA2	Stream 0	Channel 0
    // ADC1	DMA2	Stream 4	Channel 0
    // ADC2	DMA2	Stream 2	Channel 1
    // ADC2	DMA2	Stream 3	Channel 1
    // ADC3	DMA2	Stream 1	Channel 2
    // ADC3	DMA2	Stream 5	Channel 2
    // - 可以任选一个对应的 Stream，但同一时间同一 Stream 只能被一个外设占用。
    // - 如果同时用 ADC1/2/3 的 DMA，建议分别用 Stream0/2/1。
    //static const uint32_t _adc_dma_stream_map[3]  = { LL_DMA_STREAM_0 , LL_DMA_STREAM_2 , LL_DMA_STREAM_1  };
    //static const uint32_t _adc_dma_channel_map[3] = { LL_DMA_CHANNEL_0, LL_DMA_CHANNEL_1, LL_DMA_CHANNEL_2 };
    uint32_t dma_stream, dma_channel;
    
    /**/ if(dev->adc == ADC1) { dma_stream = LL_DMA_STREAM_0; dma_channel = LL_DMA_CHANNEL_0; }
    else if(dev->adc == ADC2) { dma_stream = LL_DMA_STREAM_2; dma_channel = LL_DMA_CHANNEL_1; }
    else if(dev->adc == ADC3) { dma_stream = LL_DMA_STREAM_1; dma_channel = LL_DMA_CHANNEL_2; }
    else { return STD_ERR_NOT_SUPPORTED; }

    LL_DMA_SetChannelSelection(DMA2, dma_stream, dma_channel);
    LL_DMA_ConfigTransfer(DMA2, dma_stream,
        LL_DMA_DIRECTION_PERIPH_TO_MEMORY | // 方向为 外设(ADC) -> 内存
        LL_DMA_PDATAALIGN_HALFWORD        | // 外设数据为 16 位
        LL_DMA_MDATAALIGN_HALFWORD        | // 内存数据为 16 位
        LL_DMA_PERIPH_NOINCREMENT         | // 传输过程中，设备地址不变
        LL_DMA_MEMORY_INCREMENT           | // 传输过程中，内存地址递增
        LL_DMA_PRIORITY_HIGH              | // 优先级: 高
        (looping ? LL_DMA_MODE_CIRCULAR : LL_DMA_MODE_PFCTRL) // 循环模式(DMA做为流控主体&启用循环模式) 或 外设流控(外设做为流控主体) 模式
    );
    LL_DMA_SetDataLength(DMA2, dma_stream, dev->dma_chcount * sample_count);// 数据长度为 通道数量 * 采样点数 * 采样点字节数
    LL_DMA_SetPeriphAddress(DMA2, dma_stream, (uint32_t)&dev->adc->DR);     // 外设地址为 ADC->DR 寄存器
    LL_DMA_SetMemoryAddress(DMA2, dma_stream, (uint32_t)samples);           // 内存地址为 采样缓冲区
    dev->dma_sampcnt = sample_count;
    dev->dma_buf = (uint16_t*)samples;
    
    // 使能 DMA Stream
    LL_DMA_EnableStream(DMA2, dma_stream);    
    // 启用 ADC
    LL_ADC_Enable(dev->adc);
    // 启动 ADC 转换
    LL_ADC_REG_StartConversionSWStart(dev->adc);
	return STD_ERR_OK;
}

std_err_t mcu_adc_stop(mcu_adc_t* dev) {
	// 关闭 ADC
	LL_ADC_Disable(dev->adc);
	return STD_ERR_OK;
}

uint32_t mcu_adc_curr(mcu_adc_t* dev) {
	return 0;
}
